/*
 * Copyright (C) 2009 Ionut Dediu <deionut@yahoo.com>
 *
 * Licensed under the GNU General Public License Version 2
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

 // BtDiscoverer.cpp

#include "BtDiscoverer.h"

#include <btextnotifiers.h> // KDeviceSelectionNotifierUid
#include <bt_sock.h>		// KRFCOMM

// CLASS CBtDiscoverer:

CBtDiscoverer* CBtDiscoverer::NewL(MBtDiscoveryObserver& aDiscoveryObserver)
    {
    CBtDiscoverer* self = NewLC(aDiscoveryObserver);
    CleanupStack::Pop(self);
    return self;
    }


CBtDiscoverer* CBtDiscoverer::NewLC(MBtDiscoveryObserver& aDiscoveryObserver)
    {
    CBtDiscoverer* self = new (ELeave) CBtDiscoverer(aDiscoveryObserver);
    CleanupStack::PushL(self);
    self->ConstructL();
    return self;
    }


CBtDiscoverer::CBtDiscoverer(MBtDiscoveryObserver& aDiscoveryObserver):
	CActive(CActive::EPriorityStandard),
	iDiscoveryObserver(aDiscoveryObserver),
	iIsDeviceSelectorConnected(EFalse),
	iAgent(NULL),
	iSdpSearchPattern(NULL),
	iAttrMatchList(NULL),
	iPort(-1),
	iContinueServiceDiscovery(ETrue),
	iDiscoveryState(EGettingDevice)
    {
    CActiveScheduler::Add(this);

// the service class GUID
//    0    1    2    3    4    5    6    7    8    9    10   11   12   13   14   15
// { 0xB9 0xDE 0xC6 0xD2 0x29 0x30 0x43 0x38 0xA0 0x79 0xAA 0xE5 0x60 0x05 0x32 0x38 }
// {0xB9DEC6D2 | 0x29304338 | 0xA079AAE5 | 0x60053238}
// {B9DEC6D2-2930-4338-A079-AAE560053238}

	iServiceClass[0]  = 0xB9;
	iServiceClass[1]  = 0xDE;
	iServiceClass[2]  = 0xC6;
	iServiceClass[3]  = 0xD2;
	iServiceClass[4]  = 0x29;
	iServiceClass[5]  = 0x30;
	iServiceClass[6]  = 0x43;
	iServiceClass[7]  = 0x38;
	iServiceClass[8]  = 0xA0;
	iServiceClass[9]  = 0x79;
	iServiceClass[10] = 0xAA;
	iServiceClass[11] = 0xE5;
	iServiceClass[12] = 0x60;
	iServiceClass[13] = 0x05;
	iServiceClass[14] = 0x32;
	iServiceClass[15] = 0x38;
    }


CBtDiscoverer::~CBtDiscoverer()
    {
	if(iIsDeviceSelectorConnected)
		{
		iDeviceSelector.CancelNotifier(KDeviceSelectionNotifierUid);
		iDeviceSelector.Close();
		iIsDeviceSelectorConnected = EFalse;
		}
	if(iAgent != NULL)
		{
		delete iAgent;
		iAgent = NULL;
		}
	if(iSdpSearchPattern != NULL)
		{
		delete iSdpSearchPattern;
		iSdpSearchPattern = NULL;
		}
	if(iAttrMatchList != NULL)
		{
		delete iAttrMatchList;
		iAttrMatchList = NULL;
		}

    Cancel();
    }


void CBtDiscoverer::ConstructL()
    {
    }


void CBtDiscoverer::DoCancel()
    {
    }


void CBtDiscoverer::RunL()
    {
	if(iDiscoveryState == EGettingDevice)
		{
		if(iIsDeviceSelectorConnected)
			{
			iDeviceSelector.CancelNotifier(KDeviceSelectionNotifierUid);
			iDeviceSelector.Close();
			iIsDeviceSelectorConnected = EFalse;
			}
		iDiscoveryObserver.RemoteDeviceSelected(iStatus.Int());
		}
    }


void CBtDiscoverer::SelectRemoteDevice()
	{
	iDiscoveryState = EGettingDevice;
	if(!iIsDeviceSelectorConnected)
		{
		iDeviceSelector.Connect();
		iIsDeviceSelectorConnected = ETrue;
		}
	iDeviceSelectionFilter().SetUUID(iServiceClass);
	iDeviceSelector.StartNotifierAndGetResponse(
		iStatus,
		KDeviceSelectionNotifierUid,
		iDeviceSelectionFilter,
		iDeviceSelectionResponse
		);
	SetActive();
	}

void CBtDiscoverer::FindRemoteService(TBTDevAddr btDevAddr)
	{
	iDiscoveryState = EGettingService;
	iContinueServiceDiscovery = ETrue;
	if(iAgent != NULL)
		{
		delete iAgent;
		iAgent = NULL;
		}
	if(iSdpSearchPattern != NULL)
		{
		delete iSdpSearchPattern;
		iSdpSearchPattern = NULL;
		}
	if(iAttrMatchList != NULL)
		{
		delete iAttrMatchList;
		iAttrMatchList = NULL;
		}

	iAgent = CSdpAgent::NewL(*this, btDevAddr);
	iSdpSearchPattern = CSdpSearchPattern::NewL();
	iSdpSearchPattern->AddL(iServiceClass);
	iAgent->SetRecordFilterL(*iSdpSearchPattern);
	// construct the attr match list to be used later in the attribute search
	iAttrMatchList = CSdpAttrIdMatchList::NewL();
	//iMatchList->AddL(KSdpAttrIdServiceAvailability); // Availability
	iAttrMatchList->AddL(KSdpAttrIdProtocolDescriptorList); // Port Number, RFCOMM, L2CAP

	iAgent->NextRecordRequestL();
	}

TBTDevAddr CBtDiscoverer::BTDevAddr()
	{
	return iDeviceSelectionResponse().BDAddr();
	}

const TDesC& CBtDiscoverer::BTDevName()
	{
	return iDeviceSelectionResponse().DeviceName();
	}

TBool CBtDiscoverer::IsValidBDAddr()
	{
	return iDeviceSelectionResponse().IsValidBDAddr();
	}

const TUUID& CBtDiscoverer::ServiceClass()
	{
	return iServiceClass;
	}

// from MSdpAgentNotifier:

void CBtDiscoverer::AttributeRequestComplete(TSdpServRecordHandle /* aHandle */, TInt aError)
	{
	// did not found a suitable service
	if(iContinueServiceDiscovery)
		{
		iAgent->NextRecordRequestL();
		}
	// found the remote service and extracted the RFCOMM port
	// end the search and notify the observer
	else
		{
		if(iAgent != NULL)
			{
			delete iAgent;
			iAgent = NULL;
			}
		if(iSdpSearchPattern != NULL)
			{
			delete iSdpSearchPattern;
			iSdpSearchPattern = NULL;
			}
		if(iAttrMatchList != NULL)
			{
			delete iAttrMatchList;
			iAttrMatchList = NULL;
			}
		iDiscoveryObserver.RemoteServiceFound(aError);
		}
	}

void CBtDiscoverer::AttributeRequestResult(
	TSdpServRecordHandle /* aHandle */,
	TSdpAttributeID aAttrID,
	CSdpAttrValue* aAttrValue)
	{
	if (aAttrID == KSdpAttrIdProtocolDescriptorList)
		{
		// Validate the attribute value, and extract the RFCOMM channel
		// iContinueServiceDiscovery flag will show EFalse if this parse
		// was successful and the RFCOMM port (channel) was found and set with RemotePortFound()
		TBtAttributeParser parser(*this);
		aAttrValue->AcceptVisitorL(parser);
		}
	delete aAttrValue; // Ownership has been transferred
	}

void CBtDiscoverer::NextRecordRequestComplete(
	TInt aError,
	TSdpServRecordHandle aHandle,
	TInt aTotalRecordsCount)
	{
	if(aError != KErrNone)
		{
		// service discovery ended with error;
		// delete the resources
		if(iAgent != NULL)
			{
			delete iAgent;
			iAgent = NULL;
			}
		if(iSdpSearchPattern != NULL)
			{
			delete iSdpSearchPattern;
			iSdpSearchPattern = NULL;
			}
		if(iAttrMatchList != NULL)
			{
			delete iAttrMatchList;
			iAttrMatchList = NULL;
			}

		iDiscoveryObserver.RemoteServiceFound(aError);
		return;
		}
	else if(aTotalRecordsCount == 0)
		{
		// service discovery ended with error;
		// delete the resources
		if(iAgent != NULL)
			{
			delete iAgent;
			iAgent = NULL;
			}
		if(iSdpSearchPattern != NULL)
			{
			delete iSdpSearchPattern;
			iSdpSearchPattern = NULL;
			}
		if(iAttrMatchList != NULL)
			{
			delete iAttrMatchList;
			iAttrMatchList = NULL;
			}

		iDiscoveryObserver.RemoteServiceFound(KErrNotFound);
		return;
		}
	if(aError == KErrNone && aTotalRecordsCount > 0)
		{
	    // Request the Protocol List and the availability attributes
		iAgent->AttributeRequestL(aHandle, *iAttrMatchList);
		}
	}

TInt CBtDiscoverer::Port()
	{
	return iPort;
	}

void CBtDiscoverer::RemotePortFound(TInt aPort)
	{
	iContinueServiceDiscovery = EFalse;
	iPort = aPort;
	}

// CLASS TBtAttributeParser:

TBtAttributeParser::TBtAttributeParser(
	MBtAttributeObserver& aAttributeObserver):
	iAttributeObserver(aAttributeObserver),
	iRfcommPortExpected(EFalse)
    {
    }

// from MSdpAttributeValueVisitor:

void TBtAttributeParser::EndListL()
	{
	}

void TBtAttributeParser::StartListL(CSdpAttrValueList& /*aList*/)
	{
	}

void TBtAttributeParser::VisitAttributeValueL(CSdpAttrValue &aValue, TSdpElementType aType)
	{
	if(iRfcommPortExpected)
		{
		if(aType == ETypeUint)
			{
			TUint rfcommPort = aValue.Uint();
			iAttributeObserver.RemotePortFound(rfcommPort);
			}
		iRfcommPortExpected = EFalse;
		return;
		}
	if(aType == ETypeUUID)
		{
		if(aValue.UUID() == KRFCOMM)
			{
			iRfcommPortExpected = ETrue;
			}
		}
	}
